package main

import "fmt"

/*
结构体嵌套

将一个结构体作为另一个结构体的属性（字段）。结构体嵌套可以模拟面向对象编程中的以下两种关系：

	1. 聚合关系：一个类作为另一个类的属性；
	2. 继承关系：一个类作为另一个类的子类。子类和父类的关系（TIPS：Go中没有继承，但是可以通过匿名字段模拟继承关系）
*/
func main() {

	person := Person{
		"steven",
		18,
		nil,
		School{},
	}
	fmt.Println(person)
	addr := Addr{
		"四川",
		"成都",
	}
	school := School{
		schoolName: "南高",
		schoolAddr: "四川",
		phone:      15201731579,
	}
	person.addr = &addr
	person.School = school
	fmt.Println(person)
	fmt.Println(person.addr.city, person.addr.province)
	person.addr.city = "攀枝花"
	fmt.Println(person)
	fmt.Println(person.addr.city, person.addr.province)
	addr.city = "宜宾"
	fmt.Println(person)
	fmt.Println(person.addr.city, person.addr.province)

	// 直接赋值
	person.addr = &Addr{
		"陕西省",
		"西安市",
	}
	person.School = School{
		schoolName: "建平中学",
		schoolAddr: "上海",
		phone:      15614587895,
	}
	fmt.Println(person)
	fmt.Println(person.addr.city, person.addr.province)

	// 匿名字段模拟继承关系
	bank := Bank{
		"持卡人",
		1,
	}
	fmt.Println(bank)
	// 实例化并初始化方式1
	customer := Customer{
		bank,
		"我去",
		18,
	}
	fmt.Println(customer)
	// 实例化并初始化方式2
	c := Customer{
		Bank{
			"www",
			2,
		},
		"222",
		13,
	}
	fmt.Println(c)
	// 实例化并初始化方式3
	c2 := Customer{}
	c2.age = 100
	c2.name = "姓名3"
	fmt.Println(c2)
	c2.idCard = "22335656565"
	c2.stop = 2
	fmt.Println(c2)
}

type Addr struct {
	province, city string
}

type School struct {
	schoolName string
	schoolAddr string
	phone      int
}

type Person struct {
	name string
	age  int8
	// 结构体作为属性，即嵌套结构体。一个结构体中可以嵌套包含另一个结构体或结构体指针（如果嵌套结构体属性值较多，建议用结构体指针）
	addr *Addr
	// 结构体中字段大写开头表示可公开访问，小写表示私有（仅在定义当前结构体的包中可访问）
	School School
}

type Customer struct {

	// 采用匿名字段的形式实现继承关系的效果
	//	TIPS：
	//		1. 模拟聚合关系时一定要采用有名字的结构体作为字段；
	// 		2. 结构体嵌套时，可能存在相同的成员名，成员重名会导致成员名字冲突，编译器会报错：Ambiguous reference
	Bank
	name string
	age  int8
}

type Bank struct {

	// name string		// 结构体嵌套时，可能存在相同的成员名，成员重名会导致成员名字冲突，编译器会报错：Ambiguous reference
	idCard string
	stop   byte
}
